-
Notifications
You must be signed in to change notification settings - Fork 3.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
storage: randomly mangle range key buffers in test #97222
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, thanks.
pkg/storage/pebble_mvcc_scanner.go
Outdated
// Stepping might move the iterator entirely off the bare range key, at | ||
// which point the previous curRangeKeys buffers are invalidated. | ||
p.curRangeKeys.CloneInto(&p.savedRangeKeys) | ||
p.curRangeKeys = p.savedRangeKeys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would only happen for an exhausted iterator, since we'd otherwise loop around and detect this via RangeKeyChanged()
. Why are we keeping the range keys around in this case? I think the right fix would be to set p.curRangeKeys = MVCCRangeKeyStack{}
in the if !p.iterValid()
branch below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we keeping the range keys around in this case?
In tombstone mode we still care that there was a MVCC range tombstone, even if there's no point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, for get()
, right. The implementation there is a bit hacky, there's probably a better way to do it. I think we should try to avoid doing this unnecessary copying in the general case for scans. Would be happy to have a look at it when I have a chance, if this isn't urgent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Making copies only in tombstone mode seems ok, we don't use that very often. I'll see if I can improve this separately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Submitted an alternative in #97327 which I think is slightly cleaner and only pays the cost in the one specific case that we need it (a tombstone point get).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevermind, #97327 won't work, we'll have to do something like what you're doing here. We can consider adding an isGet
field (I think we used to have one?) and only do this in that case, but there aren't currently any callers that set MVCCScanOptions.Tombstones
so it isn't particularly important right now.
60e9607
to
affb4dd
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: complete! 0 of 0 LGTMs obtained (waiting on @bananabrick and @jbowens)
pkg/storage/pebbleiter/crdb_test_on.go
line 58 at r2 (raw file):
rangeKeyBufs struct { idx int start [2][]byte
[nit] Things would be cleaner if we had an enclosing struct for these:
rangeKeyBuf struct {
start []byte
end []byte
keys []byte
}
rangeKeyBufs struct {
idx int
bufs [2]rangeKeyBuf
}
Then in the code you just do buf := &rangeKeyBufs.bufs[rangeKeyBufs.idx]
and you don't have to worry about idx again.
pkg/storage/pebbleiter/crdb_test_on.go
line 237 at r2 (raw file):
if rand.Intn(2) == 0 { idx := i.rangeKeyBufs.idx for _, b := range [...][]byte{i.rangeKeyBufs.start[idx], i.rangeKeyBufs.end[idx]} {
[nit] This looks pretty cryptic, maybe define a clear([]byte)
func and call that without the loops.
|
||
func (i *assertionIter) RangeKeys() []pebble.RangeKeyData { | ||
if !i.Valid() { | ||
panic(errors.AssertionFailedf("RangeKeys() called on !Valid() pebble.Iterator")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why isn't this valid? We document that at least RangeBounds()
returns nothing when there are no range keys, and I'd expect RangeKeys()
to do the same. Is there a compelling reason not to allow this?
pkg/storage/pebble_mvcc_scanner.go
Outdated
// Stepping might move the iterator entirely off the bare range key, at | ||
// which point the previous curRangeKeys buffers are invalidated. | ||
p.curRangeKeys.CloneInto(&p.savedRangeKeys) | ||
p.curRangeKeys = p.savedRangeKeys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Submitted an alternative in #97327 which I think is slightly cleaner and only pays the cost in the one specific case that we need it (a tombstone point get).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's still some problems this revealed that I need to work out.
Reviewable status: complete! 0 of 0 LGTMs obtained (waiting on @bananabrick and @erikgrinaker)
pkg/storage/pebbleiter/crdb_test_on.go
line 119 at r2 (raw file):
Previously, erikgrinaker (Erik Grinaker) wrote…
Why isn't this valid? We document that at least
RangeBounds()
returns nothing when there are no range keys, and I'd expectRangeKeys()
to do the same. Is there a compelling reason not to allow this?
I'm planning on restricting both RangeBounds
and RangeKeys
to require an already valid iterator with a range key. The validity checks are not trivial, including within Pebble and allowing this just duplicates them. It also makes an easier to misuse interface, because it's easy to rely on RangeBounds
/RangeKeys
without having ever checked if there was an iterator error.
bd682fb
to
4de48fa
Compare
Backport cockroachdb#90859, cockroachdb#96685 and cockroachdb#97222 to mangle the buffers returned by Pebble iterators to ensure MVCC and Cockroach code respects the Pebble iterator memory lifetimes. The RangeBounds() and RangeKeys() validity assertions are omitted, since 22.2 allowed these methods to be invoked on an invalid iterator or an iterator positioned at a point key. Release note: None Release justification: Non-production code changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reviewable status: complete! 0 of 0 LGTMs obtained (waiting on @bananabrick and @erikgrinaker)
pkg/storage/pebble_mvcc_scanner.go
line 1528 at r1 (raw file):
Previously, erikgrinaker (Erik Grinaker) wrote…
Nevermind, #97327 won't work, we'll have to do something like what you're doing here. We can consider adding an
isGet
field (I think we used to have one?) and only do this in that case, but there aren't currently any callers that setMVCCScanOptions.Tombstones
so it isn't particularly important right now.
I updated the PR to keep this copying local to get()
. This avoids the copying for scans, and also fixes another problem the earlier processRangeKeys
change did not fix: As a part of getOne
, we might call seekVersion
which can also invalidate the range keys, leaving garbage by the time get()
synthesizes the tombstone.
I think this is ready for a look again, although I still need to produce a mvcc histories test case for the seekVersion case. I've had trouble reproducing it because it has some subtle requirements.
pkg/storage/pebbleiter/crdb_test_on.go
line 58 at r2 (raw file):
Previously, RaduBerinde wrote…
[nit] Things would be cleaner if we had an enclosing struct for these:
rangeKeyBuf struct { start []byte end []byte keys []byte } rangeKeyBufs struct { idx int bufs [2]rangeKeyBuf }
Then in the code you just do
buf := &rangeKeyBufs.bufs[rangeKeyBufs.idx]
and you don't have to worry about idx again.
Done.
pkg/storage/pebbleiter/crdb_test_on.go
line 237 at r2 (raw file):
Previously, RaduBerinde wrote…
[nit] This looks pretty cryptic, maybe define a
clear([]byte)
func and call that without the loops.
Done.
4de48fa
to
dcaed6b
Compare
Randomly mangle buffers for range key bounds, suffixes and values when the iterator becomes invalid or RangeKeyChanged(). Additionally, fix two bugs surfaced by this mangling: The first bug was in pebbleMVCCScanner: The scanner could step an iterator positioned at a bare range key into an exhausted position but continue to make use of pointers to the previous iterator position's range key buffers. The second bug was in CheckSSTConflicts: The extIter was repositioned without saving or re-retrieving the range keys. Epic: None Release note: None
dcaed6b
to
d5d2eb7
Compare
Backport cockroachdb#90859, cockroachdb#96685 and cockroachdb#97222 to mangle the buffers returned by Pebble iterators to ensure MVCC and Cockroach code respects the Pebble iterator memory lifetimes. The RangeBounds() and RangeKeys() validity assertions are omitted, since 22.2 allowed these methods to be invoked on an invalid iterator or an iterator positioned at a point key. Release note: None Release justification: Non-production code changes
Fix a bug whereby CheckSSTConflicts could seek the engine iterator and seek back, assuming that the originally returned buffers were still valid. This is only true if RangeKeyChanged()=false after both seeks. Backport of part of cockroachdb#97222. Epic: None Release note: None
TFTRs! bors r=erikgrinaker |
Build succeeded: |
Randomly mangle buffers for range key bounds, suffixes and values when the
iterator becomes invalid or RangeKeyChanged().
Additionally, fix a bug in pebbleMVCCScanner surfaced by this mangling, whereby
the scanner could step an iterator positioned at a bare range key into an
exhausted position but continue to make use of pointers to the previous
iterator position's range key buffers.
Epic: None
Release note: None